unit fMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uRailroadYard, config, ExtCtrls, Menus;

const

  CURSOR_HEIGHT = DEFAULT_ITEM_HEIGHT;
  CURSOR_WIDTH = DEFAULT_ITEM_WIDTH;

type
  TMainFrm = class(TForm)
    trackLayoutImg: TImage;
    signalLightPmn: TPopupMenu;
    unsetTrainRouteMni: TMenuItem;
    STOJ1: TMenuItem;
    DN1: TMenuItem;
    AB1: TMenuItem;
    AB2: TMenuItem;
    AB3: TMenuItem;
    ZAM1: TMenuItem;
    ZAM2: TMenuItem;
    VB1: TMenuItem;
    KC1: TMenuItem;
    VC1: TMenuItem;
    VCB1: TMenuItem;
    PC1: TMenuItem;
    PP1: TMenuItem;
    RCT1: TMenuItem;
    PC21: TMenuItem;
    PN1: TMenuItem;
    RNZ1: TMenuItem;
    PPN1: TMenuItem;
    ODPO1: TMenuItem;
    signalLightNameMni: TMenuItem;
    N1: TMenuItem;
    routeCancelingPmn: TPopupMenu;
    STUJ1: TMenuItem;
    automaticallySetTrainRouteMni: TMenuItem;
    unsetTrainRouteMni2: TMenuItem;
    N2: TMenuItem;
    signalLightNameMni2: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure trackLayoutImgMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure trackLayoutImgMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure unsetTrainRouteMniClick(Sender: TObject);
    procedure trackLayoutImgMouseEnter(Sender: TObject);
    procedure trackLayoutImgMouseLeave(Sender: TObject);
    procedure unsetTrainRouteMni2Click(Sender: TObject);

  private
    { Private declarations }
    aRailroadYard: TRailroadYard;
    aRasterX,
    aRasterY: byte;
    aCursorX,
    aCursorY: Cardinal;
    aUnderCursor: TBitmap;

    procedure HideCursor;
    procedure DrawCursor(paClear: boolean = false);
    procedure ShowMenu(paMenu: TPopupMenu);
    procedure ShowRouteCancelingMenu(const paSignalLightId:Cardinal);
    procedure ShowSignalLightMenu(const paSignalLightId:Cardinal);
  public
    { Public declarations }
  end;

var
  MainFrm: TMainFrm;

implementation

uses uCanvasState;

{$R *.dfm}

procedure TMainFrm.DrawCursor(paClear: boolean);
var
  canvas: TCanvas;
  originalCanvasState: TCanvasState;
  x,y: Integer;
  cursorRect: TRect;
begin
  canvas := trackLayoutImg.Canvas;
  originalCanvasState.CopyFromCanvas(canvas);

  x := aCursorX * aRasterX;
  y := aCursorY * aRasterY;


  cursorRect := Rect(x - CURSOR_WIDTH div 2,
                     y - CURSOR_HEIGHT div 2,
                     x + CURSOR_WIDTH div 2,
                     y + CURSOR_HEIGHT div 2);

  if paClear then
  begin
    canvas.CopyRect(cursorRect,aUnderCursor.Canvas,aUnderCursor.Canvas.ClipRect);
  end
  else begin
    //save whats under the cursor
    aUnderCursor.Canvas.CopyRect(aUnderCursor.Canvas.ClipRect, canvas, cursorRect);

    //draw cursor
    canvas.Pen.Width := 1;
    canvas.Brush.Color := clFuchsia;
    canvas.Pen.Color := clFuchsia;
    canvas.Pen.Mode := pmMerge;
    canvas.Rectangle(cursorRect);
  end;

  originalCanvasState.CopyToCanvas(canvas);
end;

procedure TMainFrm.FormCreate(Sender: TObject);
begin
  aRailroadYard := TRailroadYard.Create(trackLayoutImg.Canvas);
  aRailroadYard.LoadFromFile('data/railroadYard.xml', true);
  aRailroadYard.Draw;

  aRasterX := DEFAULT_RASTER_X;
  aRasterY := DEFAULT_RASTER_Y;
  aCursorX := 1;
  aCursorY := 1;

  aUnderCursor := TBitmap.Create;
  aUnderCursor.Width  := CURSOR_WIDTH;
  aUnderCursor.Height := CURSOR_HEIGHT;

  if not aRailroadYard.ConnectToTrack then
    Application.MessageBox('Could not connect to track.', 'Connection error', MB_OK + MB_ICONWARNING);

  DrawCursor;
end;

procedure TMainFrm.FormDestroy(Sender: TObject);
begin
  aRailroadYard.Destroy;
  aUnderCursor.Destroy;
end;

procedure TMainFrm.HideCursor;
begin
  DrawCursor(true);
end;

procedure TMainFrm.ShowMenu(paMenu: TPopupMenu);
var
  menuPosition: TPoint;
begin
  HideCursor;

  menuPosition.X := trackLayoutImg.Left;
  menuPosition.Y := trackLayoutImg.Top;
  menuPosition := ClientToScreen(menuPosition);

  paMenu.Popup( menuPosition.X, menuPosition.Y);
end;

procedure TMainFrm.ShowRouteCancelingMenu(const paSignalLightId: Cardinal);
begin
  signalLightNameMni2.Caption := aRailroadYard.GetSignalLightName(paSignalLightId);
  routeCancelingPmn.Tag := paSignalLightId;
  automaticallySetTrainRouteMni.Visible := aRailroadYard.IsSignalLightMain(paSignalLightId);

  ShowMenu(routeCancelingPmn);
end;

procedure TMainFrm.ShowSignalLightMenu(const paSignalLightId: Cardinal);
begin
  signalLightNameMni.Caption := aRailroadYard.GetSignalLightName(paSignalLightId);
  signalLightPmn.Tag := paSignalLightId;

  ShowMenu(signalLightPmn);
end;

procedure TMainFrm.unsetTrainRouteMni2Click(Sender: TObject);
begin
  aRailroadYard.UnsetRouteStartingWithSignalLight(routeCancelingPmn.Tag);
  DrawCursor;
end;

procedure TMainFrm.unsetTrainRouteMniClick(Sender: TObject);
begin
  aRailroadYard.UnsetRouteStartingWithSignalLight(signalLightPmn.Tag);
  DrawCursor;
end;

procedure TMainFrm.trackLayoutImgMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  itemId: Cardinal;
begin
  case Button of

    mbLeft:
    begin
      HideCursor;
      if aRailroadYard.FindSignalLightOn(x, y, itemId)
          and aRailroadYard.IsRouteStartingWithSignalLightUsed(itemId) then
        ShowRouteCancelingMenu(itemId)
      else
        aRailroadYard.LeftMouseClicked(x,y);
      DrawCursor;
    end;

    mbRight:
    begin
      if aRailroadYard.FindSignalLightOn(x, y, itemId) then
      begin
        if not aRailroadYard.IsRouteStartingWithSignalLightUsed(itemId) then
          ShowSignalLightMenu(itemId);
      end;
    end;

    mbMiddle:
    begin
      HideCursor;
      aRailroadYard.MiddleMouseClicked(x,y);
      DrawCursor;
    end;

  end; //case
end;

procedure TMainFrm.trackLayoutImgMouseEnter(Sender: TObject);
begin
  ShowCursor(false);
end;

procedure TMainFrm.trackLayoutImgMouseLeave(Sender: TObject);
begin
  ShowCursor(true);
end;

procedure TMainFrm.trackLayoutImgMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  newCursorX, newCursorY: Cardinal;
begin
  newCursorX := round(x / aRasterX);
  newCursorY := round(y / aRasterY);

  if (aCursorX <> newCursorX) or (aCursorY <> newCursorY) then
  begin
    HideCursor;
    aCursorX := newCursorX;
    aCursorY := newCursorY;
    DrawCursor;
  end;
end;

end.
